package transport

import (
	"addsvc/internal/endpoint"
	"addsvc/internal/utils/method"
	"context"
	"encoding/json"

	"net/http"

	transporthttp "github.com/go-kit/kit/transport/http"
	"github.com/go-kit/log"
	"go.opentelemetry.io/otel"
	"go.opentelemetry.io/otel/propagation"
	"go.opentelemetry.io/otel/trace"
)

func NewHTTPHandler(endpoints *endpoint.Endpoints, logger log.Logger, tracer trace.Tracer) http.Handler {
	options := []transporthttp.ServerOption{
		transporthttp.ServerErrorLogger(logger),
		transporthttp.ServerErrorEncoder(encodeErrorResponse),
		transporthttp.ServerBefore(HTTPToContext()),
	}
	mu := http.NewServeMux()

	sum := makeEndpoint(endpoints.SumPoint, method.Sum, logger, tracer)
	mu.Handle("/sum", transporthttp.NewServer(
		sum,
		decodeHTTPSumRequest,
		encodeHTTPResponse,
		options...,
	))

	concat := makeEndpoint(endpoints.ConcatPoint, method.Concat, logger, tracer)
	mu.Handle("/concat", transporthttp.NewServer(
		concat,
		decodeHTTPConcatRequest,
		encodeHTTPResponse,
		options...,
	))

	return mu
}

func HTTPToContext() transporthttp.RequestFunc {
	return func(ctx context.Context, r *http.Request) context.Context {
		propagator := otel.GetTextMapPropagator()

		return propagator.Extract(
			ctx,
			propagation.HeaderCarrier(r.Header),
		)
	}
}

func encodeErrorResponse(_ context.Context, err error, w http.ResponseWriter) {
	w.WriteHeader(http.StatusOK)
	json.NewEncoder(w).Encode(errorWrapper{Error: err.Error()})
}

type errorWrapper struct {
	Error string `json:"error"`
}

// decodeHTTPSumRequest is a transport/http.DecodeRequestFunc that decodes a
// JSON-encoded sum request from the HTTP request body. Primarily useful in a
// server.
func decodeHTTPSumRequest(_ context.Context, r *http.Request) (any, error) {
	var req endpoint.SumRequest
	err := json.NewDecoder(r.Body).Decode(&req)
	return req, err
}

// decodeHTTPConcatRequest is a transport/http.DecodeRequestFunc that decodes a
// JSON-encoded concat request from the HTTP request body. Primarily useful in a
// server.
func decodeHTTPConcatRequest(_ context.Context, r *http.Request) (any, error) {
	var req endpoint.ConcatRequest
	err := json.NewDecoder(r.Body).Decode(&req)
	return req, err
}

func encodeHTTPResponse(_ context.Context, w http.ResponseWriter, response any) error {
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	return json.NewEncoder(w).Encode(response)
}
